/* Linux based OBD0 Honda ECU rom interpeter based on OBD0Edit. */

#include "inc/deps.h"
#include "inc/main.h"

// OpenEdit mapping variables
FILE *BinFile,*NewBinFile;
unsigned char BinObj[64000];
char HeadObj[2000];

char LnBuf[20][16] = {"-26","-24","-22","-21","-19","-18","-16","-14","-13","-11","-9","-8","-4","-1","1.3"};
char RpmBuf[20][16] = {"0500","0600","0700","0800","0900","1100","1300","1600","1900","2200","2600","3100","3700","4400","5300","6300","7400"};

long RevColdAdr=0;
long RevWarmAdr=0;
long IdleAdr=0;
long LaunchAdr=0;
long FuelMulAdr=0;
long IgnMapAdr=0;
long FuelMapAdr=0;
long ChkSumAdr;
long ChkArea;
long ChkSumMod;
int  ChkSumChk;

char BinInpFile[255];
char PMType[10];
char ProgType[20];
unsigned char FuelOrgArr[17][15];
unsigned char IgnOrgArr[17][15];
unsigned char FuelNewArr[17][15];
unsigned char IgnNewArr[17][15];
float FuelMapArr[17][15];
float IgnMapArr[17][15];
float FuelMult[2][20];
unsigned char FuelMultArr[15];
int  RevColdLim;
int  RevWarmLim;
int  Idle;
int  showhex=0;
int  Launch;
long FileLen;

int  ModelNum;

void update_checksum()
{
    int     i;
    long    ChkSum;
    int     ChkSumX;
    int     ChkSumY;
    int     ChkSumNew;

    if (ModelNum < 2)
    {
        if (BinObj[ChkSumAdr] == 0x60)
            BinObj[ChkSumAdr] = 0x80;
    }
    else
    {
        ChkSum = 0;
        for(i=0;i<ChkArea;i++)
            ChkSum = ChkSum + BinObj[i];
        ChkSumX = ChkSum % 256;
        if (ChkSumX != 0)
        {
            ChkSumY = BinObj[ChkSumMod];
            if (ChkSumX > ChkSumY)
                ChkSumNew = ChkSumY + 256 - ChkSumX;
            else
                ChkSumNew = ChkSumY - ChkSumX;
            BinObj[ChkSumMod] = ChkSumNew;
        }
        ChkSum = 0;
        for(i=0;i<ChkArea;i++)
            ChkSum = ChkSum + BinObj[i];
        ChkSumX = ChkSum % 256;
    }
}

void read_map_type()
{
    ModelNum = 100;

// **************** PM7 ************************
    if ((HeadObj[0] == '\x01') && (HeadObj[3] == '\xC0'))
    {
        sprintf(PMType,"PM7");
        // DOHC e020 & mugen
        if ((HeadObj[1] == '\x2C') && (HeadObj[3] == '\xC0'))
        {
            if (HeadObj[1473] == '\x34')
            {
                sprintf(ProgType,"D16A9 (ZC Euro)");
                ChkSumMod = 0x4000;
                ChkSumAdr = 0x23BF;
                ChkArea = 0x4000;
                ModelNum = 0;
            }

            if (HeadObj[1473] == '\x37')
            {
                sprintf(ProgType,"D16A9 (Mugen)");
                ChkSumAdr = 0x23BF;
                ChkArea = 0x4000;
                ModelNum = 1;
            }

            RevColdAdr=0x0F59;
            RevWarmAdr=0x0F53;
            IdleAdr=0x3B53;
            IgnMapAdr=0x3CE5;
            FuelMapAdr=0x3EF2;
            FuelMulAdr=0x3FF1;
        }
        // DOHC KAT  zc euro, jdm & usdm
        if ((HeadObj[1] == '\x2F') && (HeadObj[3] == '\xC0'))
        {
            if (HeadObj[31] == '\x04')
            {
                sprintf(ProgType,"D16Z5 (ZC Euro kat)");
                ChkSumMod = 0x5250;
                ChkArea = 0x5300;
                ModelNum = 2;
            }
            if ((HeadObj[31] == '\x01') && (HeadObj[32] == '\xC5'))
            {
                sprintf(ProgType,"SiR (US)");
                ChkSumMod = 0x50FC;
                ChkArea = 0x5100;
                ModelNum = 3;
            }

            if ((HeadObj[31] == '\x01') && (HeadObj[32] == '\xD5'))
            {
                sprintf(ProgType,"ZC ORG");
                ChkSumMod = 0x5250;
                ChkArea = 0x5300;
                ModelNum = 4;
            }

            if ((HeadObj[31] == '\x02') && ((HeadObj[32] == '\x7B') || (HeadObj[32] == '\x06')))
            {
                sprintf(ProgType,"Euro");
                ChkSumMod = 0x4A00;
                ChkArea = 0x5100;
                ModelNum = 5;
            }

            RevColdAdr=0x0EB2;
            RevWarmAdr=0x0EAC;
            IdleAdr=0x39F0;
            IgnMapAdr=0x3DF3;
            FuelMapAdr=0x3EF2;
            FuelMulAdr=0x3FF1;

        }
    }

// ************** PM6 ***************************
    else if (HeadObj[0] == '\x02')   // PM6 group
    {
        sprintf(PMType,"PM6");
        if ((HeadObj[2] == '\x00') && (HeadObj[3] == '\xC0'))
        {
            sprintf(ProgType,"D16A6");
            RevColdAdr=0x0DEC;
            RevWarmAdr=0x0DE6;
            IdleAdr=0x3993;
            IgnMapAdr=0x3DF3;
            FuelMapAdr=0x3EF2;
            FuelMulAdr=0x3FF1;
            ChkSumMod = 0x4A00;
            ChkArea = 0x5000;
            ModelNum = 6;
        }
        // GhettoDyne
        if ((HeadObj[2] == '\x03') && (HeadObj[3] == '\xC0'))
        {
            sprintf(ProgType,"GhettoDyne/PGMFI");
            RevColdAdr=0x0DEC;
            RevWarmAdr=0x0DE6;
            IdleAdr=0x399F;
            IgnMapAdr=0x3DFF;
            FuelMapAdr=0x3EFE;
            FuelMulAdr=0x3FFD;
            LaunchAdr=0x40EB;
            ChkSumMod = 0x438C;
            ChkArea = 0x5000;
            ModelNum = 7;
        }
        // NG63
        if ((HeadObj[2] == '\xC1') && (HeadObj[3] == '\xC0'))
        {
            sprintf(ProgType,"NG63, PM7 tables");
            RevColdAdr=0x7FE6;
            RevWarmAdr=0x7FE4;
            IdleAdr=0x7FD2;
            IgnMapAdr=0x5001;
            FuelMapAdr=0x5200;
            FuelMulAdr=0x52FF;
            LaunchAdr=0x7FFA;
            ChkSumMod = 0x438C;
            ChkArea = 0x5000;
            ModelNum = 7;
        }
    }
    else if ((HeadObj[0] == '\x01') && (HeadObj[3] == '\x02'))  // PR4 group
    {
        sprintf(PMType,"PR4");
        if ((HeadObj[0] == '\x01') && (HeadObj[3] == '\x02'))
        {
            sprintf(ProgType,"B18A1");
            RevColdAdr=0x0F53;
            RevWarmAdr=0x109B;
            IdleAdr=0x438D;
            IgnMapAdr=0x50FF;
            FuelMapAdr=0x541A;
            FuelMulAdr=0x540B;
            ChkSumMod = 0x4A00;
            ChkArea = 0x5700;
            ModelNum = 8;
        }
    }
    else if (HeadObj[0] == '\xD8')   // PW0 group
    {
        sprintf(PMType,"PW0");
        if (HeadObj[0] == '\xD8')
        {
            sprintf(ProgType,"B16A");
            RevColdAdr=0x0F59;
            RevWarmAdr=0x0F53;
            IdleAdr=0x3B53;
            // IgnMapAdr=0x3BE6; // Ignition map 2 (high cam)
            IgnMapAdr=0x3CF4;
            FuelMapAdr=0x3EF2;
            FuelMulAdr=0x3EE3;
            ChkSumAdr = 0x2045;
            ChkArea = 0x4000;
            ModelNum = 9;
        }
    }
}

void reset_variables()
{
    bzero(PMType, 10);
    bzero(ProgType, 20);
    RevWarmAdr = 0;
    IdleAdr = 0;
    LaunchAdr = 0;
}

void display()
{
    char    IgnStr[10];
    char    FuelStr[10];
    int     i,j;
    int     tcount = 0;

    //printf("ECU Code: %s, %s \nRev Limit: %d, Idle RPM: %d\n", PMType, ProgType, RevWarmLim, Idle);
    //printf(" = Timing Table = \n");
    for(i=0;i<17;i++)
    {
        printf("%d %s ", i, RpmBuf[i]);
        for(j=0;j<15;j++)
        {
            IgnMapArr[i][j] = ((IgnOrgArr[i][j] - 15.0) * 9.0) / 25.0;
            sprintf(IgnStr,"%.1f",IgnMapArr[i][j]);
            // Nice view
	    //printf("RPM: %s; MAP: %s; IGN: %s\n", RpmBuf[i], LnBuf[j], IgnStr);
            printf("%s ", IgnStr);
	    tcount++;
            if(j == 14) printf("\n");
        }
    }

    tcount = 0;

    //printf(" \n =  Fuel Table  = \n");

    //printf("fuel\n");
    for(i=0;i<17;i++)
    {
	printf("%d %s ", i, RpmBuf[i]);
        for(j=0;j<15;j++)
        {
            FuelMapArr[i][j] = (FuelOrgArr[i][j] + FuelMult[1][j]) / FuelMult[0][j];
            sprintf(FuelStr,"%.2f",FuelMapArr[i][j]);
	    // Nice view
            //printf("RPM: %s; MAP: %s; FFV: %s\n", RpmBuf[i], LnBuf[j], FuelStr);
            //printf("%d,%s,%s,%s\n", tcount, RpmBuf[i], LnBuf[j], FuelStr);
            printf("%s ", FuelStr);
            tcount++;
            if(j == 14) printf("\n");
        }
    }

    tcount = 0;
}

void read_binary_values()
{
    int i,j,k;

    if (RevWarmAdr > 0)
    {
        j = BinObj[RevWarmAdr+2];
        j += BinObj[RevWarmAdr] * 256;
        if (j > 0)
            RevWarmLim = 1920000 / j;
    }
    if (IdleAdr > 0)
    {
        j = BinObj[IdleAdr+2];
        j += BinObj[IdleAdr] * 256;
        if (j > 0)
            Idle = 1920000 / j;
    }

    if (LaunchAdr > 0)
    {
        j = BinObj[LaunchAdr+2];
        j += BinObj[LaunchAdr] * 256;
        if (j > 0)
            Launch = 1920000 / j;
    }

    k = IgnMapAdr;
    for(i=0;i<17;i++)
        for(j=0;j<15;j++,k++)
            IgnOrgArr[i][j] = BinObj[k];
    k = FuelMapAdr;
    for(i=0;i<17;i++)
        for(j=0;j<15;j++,k++)
            FuelOrgArr[i][j] = BinObj[k];

    for(k=0;k<15;k++)
    {
        if (BinObj[FuelMulAdr+k] != 0)
        {
            FuelMult[0][k] = 208.0 / (2 << (BinObj[FuelMulAdr+k]-1));
            FuelMult[1][k] = 224.0 / (2 << (BinObj[FuelMulAdr+k]-1));
        }
        else
        {
            FuelMult[0][k] = 208.0;
            FuelMult[1][k] = 224.0;
        }
        FuelMultArr[k] = BinObj[FuelMulAdr+k];
    }

    ChkSumChk = 0;
    if (ChkArea > FileLen)
        ChkArea = FileLen;
    for (i=0;i<ChkArea;i++)
    {
        ChkSumChk = ChkSumChk + BinObj[i];
    }
    ChkSumChk = ChkSumChk % 256;

}

void update_binary(char *nFile)
{
    int i,j,k,l;
    int uchar;
    int tmpfakt;
    int b1,b2;
    char tmpstr[10];
    char IgnStr[20];
    char FuelStr[20];
    float IgnFl,FuelFl;
    float FlCheck;
    int IgnInt,FuelInt;

    NewBinFile = fopen(nFile,"wb+");
    if (!(NewBinFile))
        return;

    for(i=0;i<FileLen;i++)
    {
        if ((i == RevWarmAdr) && (RevWarmAdr > 0))
        {
            tmpfakt = 1920000 / RevWarmLim;
            b1 = tmpfakt / 256;
            b2 = tmpfakt % 256;
            BinObj[i] = b1;
            BinObj[i+2] = b2;
            i+=3;
        }
        else if ((i == IdleAdr) && (IdleAdr > 0))
        {
            tmpfakt = 1920000 / Idle;
            b1 = tmpfakt / 256;
            b2 = tmpfakt % 256;
            BinObj[i] = b1;
            BinObj[i+2] = b2;
            i+=3;
        }
        else if ((i == LaunchAdr) && (LaunchAdr > 0))
        {
            tmpfakt = 1920000 / Launch;
            b1 = tmpfakt / 256;
            b2 = tmpfakt % 256;
            BinObj[i] = b1;
            BinObj[i+2] = b2;
            i+=3;
        }
        else if (IgnMapAdr == i)
        {
            for(k=0;k<17;k++)
            {
                for(l=0;l<15;l++)
                {
                    IgnFl = IgnMapArr[k][l];
                    FlCheck = IgnMapArr[k][l]-IgnFl;
                    if ((FlCheck > 0.01) || (FlCheck < -0.01))
                    {
                        IgnInt = ((IgnFl * 25) / 9) + 15;
                        BinObj[i] = IgnInt;
                    }
                    else
                        BinObj[i] = IgnOrgArr[k][l];
                    i++;
                }
            }
        }
        else if (FuelMapAdr == i)
        {
            for(k=0;k<17;k++)
            {
                for(l=0;l<15;l++)
                {
                    FuelFl = FuelMapArr[k][l];
                    FlCheck = FuelMapArr[k][l]-FuelFl;
                    if ((FlCheck > 0.01) || (FlCheck < -0.01))
                    {
                        FuelInt = (FuelFl * FuelMult[0][l]) - FuelMult[1][l];
                        BinObj[i] = FuelInt;
                    }
                    else
                        BinObj[i] = FuelOrgArr[k][l];
                    i++;
                }
            }
        }
    }

    update_checksum();


    for(i=0;i<FileLen;i++)
        fputc(BinObj[i],NewBinFile);
    fclose(NewBinFile);
}

int open_binary(char *bFile)
{
    int i=0;
    int intchr;
    int count=0;

    reset_variables();
    BinFile = fopen(bFile,"rb");
    if (!(BinFile))
        return(2);
    while (!feof(BinFile))
    {
        intchr = fgetc(BinFile);
        BinObj[i] = intchr;
        if (i<2000)
            HeadObj[i] = intchr;
        i++;

        if(showhex == 1)
        {
            if(count<15)
            {
                printf(" %02x ", intchr);
                count++;
            }

            if(count==15)
            {
                printf(" %02x \n", intchr);
                count=0;
            }
        }
    }

    printf("\n");

    fclose(BinFile);
    FileLen = i-1;

    read_map_type();
    if (ModelNum < 100)
    {
        read_binary_values();
        display();
        return(0);
    }
    else
    {
        return(1);
    }
}

void OEprint(char *msg)
{
    fprintf(stderr, ":ERROR: OpenEdit %s - %s\n", OPENEDIT_VERSION, msg);
}

int main(int argc, char **argv)
{
    int o_return = 0;

    if(argc >= 2) 
    {
        if(argc == 3 && strncmp(argv[2], "--showhex", 9))
        {
            showhex = 1;
        }

        o_return = open_binary(argv[1]);
    }
    else
    {
        o_return = 3;
    }


    if(o_return == 3)
    {
        OEprint("no filename specified. Usage: ./openedit <path/to/map.bin>");
    }
    if(o_return == 2)
    {
        OEprint("could not open binary file!");
        printf("'%s'\n", argv[1]);
        exit(2);
    }
    else if(o_return == 1)
    {
        OEprint("could not process map data, unsupported rom?");
        exit(1);
    }
    else
    {
        exit(0);
    }
}

